package org.etom.impl.ui.util;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

/**
 * An utility class which can parse different templates.
 * The keys in the template must be closed with "<>".
 * For example, "my_upload<user_name>.zip". 
 */
public class Template {
  
  private static final char START_ELEM = '<';
  private static final char END_ELEM   = '>';
  
  private String pattern;
  private String concretePattern;
  private Map<String, String> patternKeysToValues;
  private List<String> parsedPattern;
  
  private Template(String pattern) {
    if (null == pattern)
      throw new IllegalArgumentException(CommonMessages.Null_Argument);
    this.pattern = pattern;
  }
  
  /**
   * The method constructs a template from the given string.
   * 
   * @param pattern the pattern will be parsed to the template
   * 
   * @return a template which represents the patters
   */
  public static Template getInstance(String pattern) {
    return new Template(pattern);
  }

  /**
   * The method sets a new value to the key in the patter.
   * By default the value of the key is the key.
   * 
   * @param key - the key which will be replaced
   * @param value - the key will be replaced with that value
   * 
   * @return - returns the previous value of the key, null if none
   */
  public String setValue(String key, String value) {
    if (patternKeysToValues == null) {
      parsePattern();
    }
    concretePattern = null;
    return patternKeysToValues.put(key, value);
  }

  /**
   * The method returns the string representation of the template.
   * All keys which values are set will be replaced.
   */
  @Override
  public String toString() {
    if (concretePattern != null) return concretePattern;
    makeConcretePattern();
    return concretePattern;
  }

  /*
   * The method parses the pattern.
   */
  private void parsePattern() {
    if (patternKeysToValues == null) {
      patternKeysToValues = new Hashtable<String, String>();
    }
    if (parsedPattern == null) {
      parsedPattern = new ArrayList<String>();
    } else return;

    //parse the pattern
    int startElem = pattern.indexOf(START_ELEM, 0);//start from the beginning
    if (startElem == -1) {
      parsedPattern.add(pattern);//no keys in pattern
      return;
    } else {
      parsedPattern.add(pattern.substring(0, startElem));
    }
    int endElem;
    int prevEndElem = endElem = startElem - 1;
    String currentKey;
    while (true) {
      prevEndElem = endElem;
      //"example<>" - is not valid key or no more start elements
      if ((startElem == -1) || (startElem >= pattern.length() - 2)) {
        parsedPattern.add(pattern.substring(prevEndElem + 1));
        break;
      }
      endElem = pattern.indexOf(END_ELEM, startElem + 2); //at least one char in <>
      if (endElem == -1) {
        parsedPattern.add(pattern.substring(prevEndElem + 1));//it is only < (without >)
        break;
      } else {
        currentKey = pattern.substring(startElem, endElem + 1);
        parsedPattern.add(currentKey);//add key
        patternKeysToValues.put(currentKey, currentKey);
      }
      startElem = pattern.indexOf(START_ELEM, endElem + 1);//search next on next position
      if (startElem > endElem + 1) {//string between two keys (<...>aaaa<...>)
        parsedPattern.add(pattern.substring(endElem + 1, startElem));
      }
    }
  }
  
  /*
   * The method constructs the concrete string representation of
   * the template.
   */
  private void makeConcretePattern() {
    if (parsedPattern == null) {
      parsePattern();
    }
    StringBuilder strBuilder = new StringBuilder();
    String currentKeyValue;
    for (String currentElem : parsedPattern) {
      currentKeyValue = patternKeysToValues.get(currentElem);
      strBuilder.append((currentKeyValue != null) ?
          currentKeyValue : currentElem);
    }
    concretePattern = strBuilder.toString();
  }
  
}
